/*write.cpp
 *This program writes specified file to specified block on created RAID.
 *Created on: 2013.12.24
 *Author: Petrunin D.S.
 */
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
//-----------------
#include "base.h"


int writeRAID01(FILE* file, int fileS, int blockN, int nDisks, int nBlocks, int blockS) {
	if (mapping(nDisks, nBlocks, blockS, false) == 1)
		return 1;
	int fileblockS = fileS * 2 / nDisks;
	int offset = (blockN - 1) * blockS;
	//RAID0
	for (int i = 1; i <= (nDisks / 2); i++) {
		if (i == (nDisks / 2))
			fileblockS += (fileS % (nDisks / 2));
		fread(mappedDataArray[i] + offset, 1, fileblockS, file);
		memset(mappedDataArray[i] + offset + fileblockS, 0, blockS - fileblockS);
	}
	//Mirroring
	for(int i = (nDisks / 2) + 1; i <= nDisks; i++)
		memcpy(mappedDataArray[i] + offset, mappedDataArray[i - (nDisks / 2)] + offset, blockS);
	fclose(file);
	if (unmapping(nDisks, nBlocks, blockS, false) == 1)
		return 1;
	return 0;
}

int writeRAID10(FILE* file, int fileS, int blockN, int nDisks,	int nBlocks, int blockS) {
	if (mapping(nDisks, nBlocks, blockS, false) == 1)
		return 1;
	int fileblockS = (fileS * 2) / nDisks;
	int offset = (blockN - 1) * blockS;
	for (int i = 1; i < nDisks; i += 2) {
		if (i == (nDisks - 1))
			fileblockS += (fileS % (nDisks / 2));
		fread(mappedDataArray[i] + offset, 1, fileblockS, file);
		memset(mappedDataArray[i] + offset + fileblockS, 0, blockS - fileblockS);
	}
	for (int i = 2; i <= nDisks; i += 2)
		memcpy(mappedDataArray[i] + offset, mappedDataArray[i - 1] + offset, blockS);
	fclose(file);
	if (unmapping(nDisks, nBlocks, blockS, false) == 1)
		return 1;
	return 0;
}


int main(int argc, char* argv[]) {

	if (argc < 3) {
		cout << "Not enough parameters\n";
		return 1;
	}
	FILE* config = fopen("conf", "r");
	if (config == NULL) {
		cout << "Can't open config file.\n";
		return 1;
	}
	int nDisks, nBlocks, blockS, RAIDLevel;
	fscanf(config, "%i\n%i\n%i\n%i\n", &nDisks, &nBlocks, &blockS,	&RAIDLevel);
	fclose(config);
	if (failedDisk(nDisks, false)) {
		cout << "Rebuild RAID.\n";
		return 1;
	}
	int blockN = atoi(argv[1]);
	if ((blockN == 0) || (blockN > nBlocks)) {
		cout << "Number of block must be in range 1.." << nBlocks << ".\n";
		return 1;
	}
	char* fileName = argv[2];
	struct stat file_stat;
	int statf = stat(fileName, &file_stat);
	if (statf != 0) {
		cout << "Can't get statistics of file " << fileName << ".\n";
		return 1;
	}
	if (file_stat.st_size > blockS) {
		cout << "Size of file must be <= " << blockS << ".\n";
		return 1;
	}
	FILE * file;
	file = fopen(fileName, "rb+");
	if (file == NULL) {
		cout << "Can't open file " << file << ".\n";
		return 1;
	}

	switch (RAIDLevel) {
	case 0:
		return writeRAID01(file, file_stat.st_size, blockN, nDisks,
				nBlocks, blockS);
	case 1:
		return writeRAID10(file, file_stat.st_size, blockN, nDisks,
				nBlocks, blockS);
	}
}

